<template>
  <view>
    <view @click="toggle('on')">
      <slot></slot>
    </view>
    <view v-if="show.io" class="">
      <uni-transition :show="show.picker" :duration="time" mode-class="fade" custom-class="u-ps-f u-ps-full u-mask" @click="toggle('off')"></uni-transition>
      <uni-transition :show="show.picker" :duration="time" :mode-class="['slide-bottom', 'fade']" custom-class="u-w-100 u-bg-white u-radius-tl-20rp u-radius-tr-20rp u-ps-a u-ps-b0">
        <!-- 头部 -->
        <view class="u-flex u-flex-ai-c u-flex-jc-sb u-pd-lr-16rp">
          <view>取消</view>
          <view class="u-flex u-flex-ai-c u-flex-jc-sa" style="min-width: 42%" @click="scrollTo(0, Number(scrollTop[0]) + $craft.rpx2px(80))">
            <view v-for="(item, index) in selected" :key="index">{{ item.value.value }}</view>
          </view>
          <view @click="select">确认</view>
        </view>
        <!-- 本体 -->
        <view v-if="!isGetRes" class="u-ps-r u-flex u-flex-ai-c u-pd-lr-16rp">
          <!-- 蒙版层 -->
          <view class="u-ps-a u-ps-center u-bd-tb-e5e u-w-100 u-h-80rp u-z-hide"></view>
          <scroll-view class="u-flex-1 u-of-scroll" :style="'height:' + item_h * item_num + 'rpx;'" v-for="(row, x) in data" :key="x" scroll-y="true" :scroll-top="scrollTop[x]" @scroll="scrollEvent(x, $event)" @touchstart="touching = true" @touchend="rowTouchend(x)" @scrolltoupper="toupper(x, row.length)" @scrolltolower="tolower(x, row.length)" :scroll-with-animation="isAnima">
            <view :style="'padding:' + ((item_num - 1) / 2) * item_h + 'rpx 0;'">
              <view class="u-flex u-flex-ai-c u-flex-jc-c u-h-80rp u-f-36rp u-t-999" :class="selected[x] && selected[x].index === y ? 'u-f-b u-t-333' : ''" v-for="(item, y) in row" :key="y" @click="clickSel(x, y)">{{ item.value }}</view>
            </view>
          </scroll-view>
        </view>
        <!-- 尾部 -->
      </uni-transition>
    </view>
  </view>
</template>

<script>
import { getPickerData } from "./datedata.js";
export default {
  name: "es-picker",
  data() {
    return {
      /* 公域传入 input */
      native: [], // 原生数据列
      time: 300, // 动画过渡时长
      item_h: 80, // 单元高度
      item_num: 7, // 屏内单元数量
      isGetRes: false, // 选择是否走请求
      isStage: false, // 是否为分阶段性的
      initData: [], // 初始选中
      type: "", // 传入
      /* 公域返回 output */
      selected: [], // 已选中数据
      /* 私域数据 */
      data: [], // 处理后的数据列
      timeout: [], // 定时器（防抖）
      scrollTop: {}, // 滚动高度
      scroll_live: {}, // 滚动高度缓存
      touching: false, // 触摸中（节流）
      scrolling: false, // 滚动中（节流）
      show: { io: false, picker: false }, // 开关
      isAnima: true,
      loop: {
        on: false,
        scope: 6,
        min: [],
        max: [],
        ing: false
      }
    };
  },
  mounted() {
    this.type = "time"; // 调试模式
    this.toggle("on"); // 调试模式
    this.init();
  },
  methods: {
    // picker 开关
    toggle(n) {
      if (n === "on") {
        this.show.io = true;
        this.show.picker = true;
      } else if (n === "off") {
        this.show.picker = false;
        setTimeout(() => {
          this.show.io = false;
        }, this.time);
      }
    },
    // picker 初始化
    init() {
      if (this.type === "time") {
        let now = new Date();
        this.loop.on = true; // 调试模式
        this.initData = [now.getHours(), now.getMinutes(), now.getSeconds()]; // 调试模式
        this.native = getPickerData.time(true, true);
        this.time_ctrl(0);
      }
    },
    // time 选择器 初始化
    time_ctrl(x) {
      if (this.loop.on) {
        // 循环模式，数组拼接大法
        let l_start = [...this.native[x].slice(this.native[x].length - this.loop.scope, this.native[x].length)];
        let l_end = [...this.native[x].slice(0, this.loop.scope)];
        this.data[x] = l_start.concat(this.native[x], l_end);
        // 设置循环触发阈值
        this.loop.min[x] = this.computer(null, this.loop.scope);
        this.loop.max[x] = this.computer(null, this.data[x].length - this.loop.scope);
      } else this.data[x] = loopData;
      x++;
      if (x > this.native.length - 1) this.createPickerRow();
      else this.time_ctrl(x);
    },
    // 创建初始化滚动高度 及 初始化选中态
    createPickerRow() {
      if (!this.isGetRes) {
        if (this.type === "time")
          for (var i = 0; i < this.data.length; i++) {
            // 计算 initData 所在位置
            let initFix; // 初始 x
            if (!this.initData[i]) this.initData[i] = 0; // 如果没有初始init, 自动赋值为0
            if (!this.loop.on) initFix = this.initData[i];
            else initFix = this.initData[i] + this.loop.scope;
            let initFixer = this.computer(null, initFix);
            this.$set(this.scrollTop, i, initFixer.i);
            this.$set(this.scroll_live, i, initFixer.i);
            this.timeout.push("");
            if (!this.loop.on) this.selected.push({ index: initFixer.i, value: this.data[i][initFixer.i] });
            else this.fixed(i, initFixer.i); // this.selected.push({ index: this.loop.scope, value: this.data[i][0] });
          }
      } else {
      }
    },
    // 触顶 仅非循环
    toupper(x, length) {
      if (this.loop.on) return false;
      this.select(x, 0);
    },
    // 触底 仅非循环
    tolower(x, length) {
      if (this.loop.on) return false;
      this.select(x, length - 1);
    },
    // 滚动事件（已处理循环）
    scrollEvent(i, e) {
      console.log(this.loop.ing);
      if (this.loop.ing) console.log("e.preventDefault();");
      this.scrolling = true;
      clearTimeout(this.timeout[i]); // 防抖
      this.scroll_live[i] = e.detail.scrollTop; // 记录实时滚动高度，用于解决UNI scrollTop缓存问题
      if (this.loop.on && !this.loop.ing) {
        // 循环计算
        if (e.detail.scrollTop < this.loop.min[i].h) this.noAniTo(i, this.loop.max[i].i);
        if (e.detail.scrollTop > this.loop.max[i].h) this.noAniTo(i, this.loop.min[i].i);
      }
      this.timeout[i] = setTimeout(() => {
        this.scrolling = false;
        if (!this.touching) this.fixed(i); // 鼠标滚动 节流 定位器
      }, 180);
    },
    // 触摸结束事件
    rowTouchend(i) {
      this.touching = false;
      if (!this.scrolling) this.fixed(i);
    },
    // 高度/下标 计算器
    computer: function (x, y) {
      let item_h_px = this.$craft.rpx2px(this.item_h); // 获取item_h的px单位高度
      let fixIndex = y != undefined ? y : Math.round(this.scroll_live[x] / item_h_px); // 计算index
      let fixPos = fixIndex * item_h_px; // 计算修正后的偏移量
      return { h: fixPos, i: fixIndex };
    },
    // 滚动定位器
    fixed(i, y) {
      this.scrollTop[i] = this.scroll_live[i]; // 处理view层不同步的问题
      let fixer = this.computer(i, y);
      if (this.selected[i] != this.data[i][fixer.i]) this.select(i, fixer.i); // 赋予选中
      if (!this.loop.ing && fixer.h === this.scrollTop[i]) return false; // 阻止修正滚动高度所造成的二次滚动
      this.$nextTick(() => {
        this.scrollTop[i] = fixer.h; // 定位修正
      });
    },
    // 点击选中（自动跳转 - 已处理循环）
    clickSel(x, y) {
      if (!this.loop.on) this.fixed(x, y);
      else {
        // 循环长度: this.loop.scope
        // 条目总长度: data[x].length
        // 最小阈值下标: loop.min[x].i
        // 最大阈值下标: loop.max[x].i
        // 跳转前所在位置: selected[x].index
        if (y < this.loop.min[x].i) {
          // 小于最小阈值
          let fakeY = this.loop.max[x].i + (this.selected[x].index - this.loop.min[x].i); // 计算需要在非动画下跳转到指定的假位置
          let trueY = this.data[x].length - (this.loop.min[x].i - y) - this.loop.scope; // 计算真位置
          this.noAniTo(x, fakeY, trueY); // 闪至最大阈值之外fakeY，动画从fakeY调整至最大阈值之内的合法位置trueY
        } else if (y > this.loop.max[x].i) {
          let fakeY = this.loop.scope - (this.loop.max[x].i - this.selected[x].index);
          let trueY = y - this.loop.max[x].i + this.loop.scope;
          this.noAniTo(x, fakeY, trueY);
        } else this.fixed(x, y);
      }
    },
    // 非动画的跳转（循环处理）
    noAniTo(x, y, aniY) {
      this.loop.ing = true;
      console.log(121);
      this.isAnima = false;
      this.scrollTop[x] = this.scroll_live[x]; // 处理view层不同步的问题
      let fixer = this.computer(x, y);
      this.$nextTick(() => {
        this.scrollTop[x] = fixer.h;
        if (!aniY) this.select(x, y);
        setTimeout(() => {
          this.isAnima = true;
          this.loop.ing = false;
          if (aniY) this.fixed(x, aniY);
        });
      });
    },
    // 选择态
    select(x, y, z) {
      this.selected[x] = { index: y, value: this.data[x][y] };
      if (this.isGetRes) {
        getRes(x + 1);
      }
    },
    getRes(x) {
      if (x > this.data.length - 1) return false;
      this.$emit("picknext", data);
    },
    // 确定
    done() {
      this.$emit("pickit", data);
    }
  }
};
</script>

<style scoped></style>
